﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.IO;
using Dicom.IO.Buffer;
using Dicom.Imaging;

namespace Dicom.Dump {
	public partial class MainForm : Form {
		private string _fileName;
		private string _path;

		private List<string> dcm = new List<string>();

		private int index = 0;

		private bool isHeader;

		private int _x = 0, _y = 0;

#region DICOM Processing

		public MainForm() {
			InitializeComponent();
			imgPreview.MouseClick += new MouseEventHandler(MouseClickHandler);
		}

		private void Reset(bool Header) {
			lvDicom.Items.Clear();

			if (Header)
			{
				dcm.Clear();
				index = 0;
			}
		}

		private delegate void AddItemDelegate(string tag, string vr, string length, string value);

		private void AddItem(string tag, string vr, string length, string value) {
			if (InvokeRequired) {
				BeginInvoke(new AddItemDelegate(AddItem), tag, vr, length, value);
				return;
			}

			var lvi = lvDicom.Items.Add(tag);

			if (tag.Contains("Referenced File ID") && isHeader)
			{
				dcm.Add(value);
			}
			lvi.SubItems.Add(vr);
			lvi.SubItems.Add(length);
			lvi.SubItems.Add(value);
		}

		private void LoadTags(string fname)
		{
			try
			{
				lvDicom.BeginUpdate();

				_fileName = fname;

				if (_fileName.EndsWith("\\DICOMDIR"))
				{
					isHeader = true;
					FileInfo fi = new FileInfo(_fileName);
					_path = fi.DirectoryName;
				}
				else
				{
					isHeader = false;
				}

				Reset(isHeader);

				var file = DicomFile.Open(_fileName);
				new DicomDatasetWalker(file.FileMetaInfo).Walk(new DumpWalker(this));
				new DicomDatasetWalker(file.Dataset).Walk(new DumpWalker(this));
			}
			catch (Exception ex)
			{
				MessageBox.Show(this, "Exception while loading DICOM file: " + ex.Message, "Error loading DICOM file", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			finally
			{
				lvDicom.EndUpdate();
			}
		}

		private void OnClickOpen(object sender, EventArgs e)
		{
			var ofd = new OpenFileDialog();
			ofd.AddExtension = false;
			ofd.Title = "Open DICOMDIR";
			ofd.FileName = "";
			ofd.Filter = "DICOM Directory|DICOMDIR|All Files (*.*)|*.*";
			if (ofd.ShowDialog() == DialogResult.Cancel)
				return;

			LoadTags(ofd.FileName);
		}

		private class DumpWalker : IDicomDatasetWalker {
			int _level = 0;

			public DumpWalker(MainForm form) {
				Form = form;
				Level = 0;
			}

			public MainForm Form {
				get;
				set;
			}

			public int Level {
				get { return _level; }
				set {
					_level = value;
					Indent = String.Empty;
					for (int i = 0; i < _level; i++)
						Indent += "    ";
				}
			}

			private string Indent {
				get;
				set;
			}

			public void OnBeginWalk(DicomDatasetWalker walker, DicomDatasetWalkerCallback callback) {
			}

			public bool OnElement(DicomElement element) {
				var tag = String.Format("{0}{1}  {2}", Indent, element.Tag.ToString().ToUpper(), element.Tag.DictionaryEntry.Name);

				string value = "<large value not displayed>";
				if (element.Length <= 2048)
					value = String.Join("\\", element.Get<string[]>());

				if (element.ValueRepresentation == DicomVR.UI && element.Count > 0) {
					var uid = element.Get<DicomUID>(0);
					var name = uid.Name;
					if (name != "Unknown")
						value = String.Format("{0} ({1})", value, name);
				}

				Form.AddItem(tag, 
					element.ValueRepresentation.Code, 
					element.Length.ToString(), 
					value);
				return true;
			}

			public bool OnBeginSequence(DicomSequence sequence) {
				var tag = String.Format("{0}{1}  {2}", Indent, sequence.Tag.ToString().ToUpper(), sequence.Tag.DictionaryEntry.Name);

				Form.AddItem(tag, "SQ", String.Empty, String.Empty);

				Level++;
				return true;
			}

			public bool OnBeginSequenceItem(DicomDataset dataset) {
				var tag = String.Format("{0}Sequence Item:", Indent);

				Form.AddItem(tag, String.Empty, String.Empty, String.Empty);

				Level++;
				return true;
			}

			public bool OnEndSequenceItem() {
				Level--;
				return true;
			}

			public bool OnEndSequence() {
				Level--;
				return true;
			}

			public bool OnBeginFragment(DicomFragmentSequence fragment) {
				var tag = String.Format("{0}{1}  {2}", Indent, fragment.Tag.ToString().ToUpper(), fragment.Tag.DictionaryEntry.Name);

				Form.AddItem(tag, fragment.ValueRepresentation.Code, String.Empty, String.Empty);

				Level++;
				return true;
			}

			public bool OnFragmentItem(IByteBuffer item) {
				var tag = String.Format("{0}Fragment", Indent);

				Form.AddItem(tag, String.Empty, item.Size.ToString(), String.Empty);
				return true;
			}

			public bool OnEndFragment() {
				Level--;
				return true;
			}

			public void OnEndWalk() {
			}
		}

		private void btnNext_Click(object sender, EventArgs e)
		{
			// do stuff
			if (index < dcm.Count)
			{
				index += 1;
			}
			try
			{
				DicomFile df = DicomFile.Open(_path+ "\\" + dcm[index]);
				DicomImage img = new DicomImage(df.Dataset);
				Image i;
				i = img.RenderImage();
				imgPreview.Image = i;

				// load the tags
				LoadTags(_path + "\\" + dcm[index]);
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		private void btnPrevious_Click(object sender, EventArgs e)
		{
			// do some more stuff
			if (index > 0)
			{
				index -= 1;
			}
			try
			{
				DicomFile df = DicomFile.Open(_path + "\\" + dcm[index]);
				DicomImage img = new DicomImage(df.Dataset);
				Image i;
				i = img.RenderImage();
				imgPreview.Image = i;

				// load the tags
				LoadTags(_path + "\\" + dcm[index]);
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

#endregion

		private void selectOverlayImageToolStripMenuItem_Click(object sender, EventArgs e)
		{
			dlgOpenFile.AddExtension = true;
			dlgOpenFile.CheckFileExists = true;
			dlgOpenFile.CheckPathExists = true;
			dlgOpenFile.Multiselect = false;
			dlgOpenFile.FileName = "";
			dlgOpenFile.Title = "Open Overlay";
			dlgOpenFile.Filter = "DICOM Image(*.dcm)|*.dcm|All Files (*.*)|*.*";

			if (dlgOpenFile.ShowDialog() == DialogResult.Cancel)
				return;
			try
			{
				DicomFile df = DicomFile.Open(dlgOpenFile.FileName);
				DicomImage img = new DicomImage(df.Dataset);
				Image overlay;
				overlay = img.RenderImage();
				Bitmap bmpOverlay = new Bitmap(overlay);
				Image source = imgPreview.Image;
				Bitmap bmpSource = new Bitmap(source);

				Bitmap bSource = bmpSource.Clone(new Rectangle(0, 0, source.Width, source.Height),
												 PixelFormat.Format24bppRgb);
				Bitmap bOverlay = bmpOverlay.Clone(new Rectangle(0, 0, overlay.Width, overlay.Height),
												   PixelFormat.Format24bppRgb);
				double alpha, beta;

				if (!Double.TryParse(txtAlpha.Text, out alpha))
				{
					alpha = 1;
					MessageBox.Show("Value of alpha should be a valid double precision floating point value.", "Error");
				}

				if (!Double.TryParse(txtBeta.Text, out beta))
				{
					beta = 0;
					MessageBox.Show("Value of beta should be a valid double precision floating point value.", "Error");
				}
				
				Bitmap result = WeightedOverlay(bSource, bOverlay, alpha, beta, _x, _y);
				Image final = result;
				imgPreview.Image = final;
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		/// <summary>
		/// Function for weighted overlay filter.
		/// </summary>
		/// <param name="source">24 bit RGB source bitmap</param>
		/// <param name="overlay">24 bit RGB overlay bitmap</param>
		/// <param name="alpha">weight (0.0 to 1.0) of source</param>
		/// <param name="beta">weight (0.0 to 1.0) of overlay</param>
		/// <param name="x">X coordinate of overlay</param>
		/// <param name="y">Y coordinate of overlay</param>
		/// <returns>24 bit RGB bitmap, resulting after applying weighted overlay</returns>
		private Bitmap WeightedOverlay(Bitmap source, Bitmap overlay, double alpha, double beta, int x=0, int y=0)
		{
			Bitmap final = new Bitmap(source);

			int a = x, b = y;

			if (((alpha < 0.0) || (alpha > 1.0)) || ((beta < 0.0) || (beta > 1.0)))
				return final;
			

			// bitmaps should be 24 bit RGB
			if ((source.PixelFormat != PixelFormat.Format24bppRgb) || 
				(overlay.PixelFormat != PixelFormat.Format24bppRgb))
			{
				return final;
			}

			/*
			 * Weighted overlay calculation
			 */

			if (a + overlay.Width > source.Width)
			{
				a = source.Width - overlay.Width;
			}

			if (b + overlay.Height > source.Height)
			{
			    b = source.Height - overlay.Height;
			}

			int width = overlay.Width;
			int height = b + overlay.Height;

			Color c = new Color();
			Color c1 = new Color();
			Color c2 = new Color();

			for (int i = 0; i < width; ++i )
			{
				for (int j = 0; j < height; ++j)
				{
					c1 = source.GetPixel(i + a, j + b);
					c2 = overlay.GetPixel(i, j);

					// reduce G and B from overlay, result will be R shade
					int _blue = c2.B - 100;
					int _green = c2.G - 100;

					if (_blue < 0)
					{
						_blue = 0;
					}
					if (_green < 0)
					{
						_green = 0;
					}

					int red = (int) ((alpha*c1.R) + (beta*c2.R));
					int blue = (int) ((alpha*c1.B) + (beta*_blue));
					int green = (int)((alpha * c1.G) + (beta * _green));

					if (red > 255)
						red = 255;
					if (blue > 255)
						blue = 255;
					if (green > 255)
						green = 255;

					c = Color.FromArgb(red, green, blue);
					final.SetPixel(i+a, j+b, c);
				}
			}

			return final;
		}

		private void MouseClickHandler(object sender, MouseEventArgs e)
		{
			_x = e.Location.X;
			_y = e.Location.Y;
			MessageBox.Show("X: " + _x.ToString() + " Y: " + _y.ToString(), "Value");
		}

		private void openDCMToolStripMenuItem_Click(object sender, EventArgs e)
		{
			var ofd = new OpenFileDialog();
			ofd.AddExtension = false;
			ofd.Title = "Open DCM Image";
			ofd.FileName = "";
			ofd.Filter = "DCM Image (*.dcm)|*.dcm|All Files (*.*)|*.*";
			if (ofd.ShowDialog() == DialogResult.Cancel)
				return;

			try
			{
				DicomFile df = DicomFile.Open(ofd.FileName);
				DicomImage img = new DicomImage(df.Dataset);
				Image i;
				i = img.RenderImage();
				imgPreview.BackgroundImage = i;

				// load the tags
				LoadTags(ofd.FileName);
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}
	}
}
